package com.passion.mq.rabbitmq;

import com.passion.mq.config.RabbitMqConfig;
import lombok.Data;
import org.apache.commons.compress.utils.Lists;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.Declarable;
import org.springframework.amqp.core.Declarables;
import org.springframework.amqp.core.Exchange;
import org.springframework.amqp.core.ExchangeBuilder;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.core.QueueBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 类描述：rabbitmq的业务队列自动创建配置类
 *
 * @author ZhuYin
 * @since 2024年12月29日
 */
@Data
@Configuration
@ConditionalOnBean(RabbitMqConfig.class)
@ConfigurationProperties(prefix = "archives.mq")
public class BusinessQueAutoCreateConfig {

    private List<MqBusinessQueue> business;
    /**
     * 用于存储已经创建的队列和交换机
     */
    private final Map<String, Queue> queueMap = new HashMap<>();
    private final Map<String, Exchange> exchangeMap = new HashMap<>();

    /**
     * 方法说明：声明业务队列
     *
     * @return 返回队列声明
     * @author ZhuYin
     * @since 2024年12月29日
     */
    @Bean
    @ConditionalOnBean(RabbitMqConfig.class)
    public Declarables declareBusinessQueues() {
        // 创建队列并将它们存储在 queueMap 中
        business.forEach(queueConfig -> {
            Map<String, Object> arguments = queueConfig.getArguments();
            for (Map.Entry<String, Object> entry : arguments.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();
                // 特殊参数处理，否则会报错
                String integerKey = "x-message-ttl";
                if (key.equalsIgnoreCase(integerKey)) {
                    // 该参数的配置值要转成整数
                    arguments.put(key, Integer.parseInt(value + ""));
                }
            }
            Queue queue = QueueBuilder.durable(queueConfig.getQueueName()).withArguments(arguments).build();
            queueMap.put(queueConfig.getQueueName(), queue);
        });
        return new Declarables(queueMap.values().toArray(new Declarable[0]));
    }

    /**
     * 方法说明：声明业务交换机，使用direct模式
     *
     * @return 返回业务交换机声明
     * @author ZhuYin
     * @since 2024年12月29日
     */
    @Bean
    @ConditionalOnBean(RabbitMqConfig.class)
    public Declarables declareBusinessExchanges() {
        // 创建交换机并将它们存储在 exchangeMap 中
        business.forEach(queueConfig -> {
            String exchangeName = queueConfig.getQueueName() + "-exchange";
            Exchange exchange = ExchangeBuilder.directExchange(exchangeName).durable(true).build();
            exchangeMap.put(exchangeName, exchange);
        });
        return new Declarables(exchangeMap.values().toArray(new Declarable[0]));
    }

    /**
     * 方法说明：将业务队列和交换机进行绑定
     *
     * @return 返回绑定声明
     * @author ZhuYin
     * @since 2024年12月29日
     */
    @Bean
    @ConditionalOnBean(RabbitMqConfig.class)
    public Declarables declareBusinessBindings() {
        // 创建绑定
        List<BindingConfig> bindings = Lists.newArrayList();
        for (MqBusinessQueue queueConfig : business) {
            String queueName = queueConfig.getQueueName();
            String exchangeName = queueName + "-exchange";
            BindingConfig bindingConfig = new BindingConfig();
            bindingConfig.setQueue(queueName);
            bindingConfig.setExchange(exchangeName);
            bindingConfig.setRoutingKey(queueName);
            bindings.add(bindingConfig);
        }

        return new Declarables(bindings.stream()
                .map(this::createBinding)
                .toArray(Declarable[]::new));
    }

    private Binding createBinding(BindingConfig bindingConfig) {
        // 从 queueMap 和 exchangeMap 中获取已经创建的队列和交换机
        Queue queue = queueMap.get(bindingConfig.getQueue());
        if (queue == null) {
            throw new IllegalArgumentException("Queue not found: " + bindingConfig.getQueue());
        }

        Exchange exchange = exchangeMap.get(bindingConfig.getExchange());
        if (exchange == null) {
            throw new IllegalArgumentException("Exchange not found: " + bindingConfig.getExchange());
        }
        // 创建绑定
        return BindingBuilder.bind(queue)
                .to(exchange)
                .with(bindingConfig.getRoutingKey())
                .noargs();
    }

    @Data
    public static class MqBusinessQueue {
        /**
         * 业务描述
         */
        private String description;
        /**
         * 队列名称
         */
        private String queueName;
        /**
         * 消费者的并发数
         */
        private Integer consumerConcurrency;
        /**
         * 其他的队列设置参数
         */
        private Map<String, Object> arguments = new HashMap<>();
    }

    @Data
    public static class BindingConfig {
        private String queue;
        private String exchange;
        private String routingKey;
    }

}
